package com.cheney.common.sensitive.handler;

import ch.qos.logback.classic.spi.ILoggingEvent;
import com.cheney.common.sensitive.strategy.SensitiveStrategy;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * 抽象的正则脱敏器
 */
public abstract class AbstractLogbackMessageHandler<KeyMatcher> implements LogbackMessageHandler {

    protected final Map<KeyMatcher, SensitiveStrategy> strategies;

    protected final List<LogbackMessageHandler> plugins = new ArrayList<>();

    public AbstractLogbackMessageHandler(Map<KeyMatcher, SensitiveStrategy> strategies, List<LogbackMessageHandler> plugins) {
        validate(strategies);
        if (!CollectionUtils.isEmpty(plugins)) {
            this.plugins.addAll(plugins);
        }
        this.strategies = Collections.unmodifiableMap(strategies);
    }

    @Override
    public void desensitize(final ILoggingEvent event, final String message, final StringBuilder buffer) {
        // 脱敏器插件
        Optional<LogbackMessageHandler> firstMatchedPlugin = this.plugins.stream()
                .filter(x -> x.support(event))
                .findFirst();
        if (firstMatchedPlugin.isPresent()) {
            firstMatchedPlugin.get().desensitize(event, message, buffer);
        } else {
            // 脱敏器
            if (support(event)) {
                doDesensitize(event, buffer, strategies);
            } else {
                buffer.append(message);
            }
        }
    }

    /**
     * 脱敏
     *
     * @param event       日志事件
     * @param buffer      日志信息容器，用于存放最终的日志
     * @param strategyMap 脱敏字段匹配器 及 脱敏策略映射关系
     *                    <ul>
     *                        <li>key   - 用于匹配需要脱敏的字段</li>
     *                        <li>value - 对当前key匹配出来的字段，进行当前策略的脱敏</li>
     *                    </ul>
     */
    protected abstract void doDesensitize(final ILoggingEvent event, final StringBuilder buffer, final Map<KeyMatcher, SensitiveStrategy> strategyMap);

    /**
     * 策略合法性校验
     */
    protected void validate(Map<KeyMatcher, SensitiveStrategy> strategies) {
        Assert.isTrue(!CollectionUtils.isEmpty(strategies), "sensitive strategies cannot be empty.");
        strategies.values().forEach(strategy -> {
            String category = strategy.category();
            if (!StringUtils.hasText(category)) {
                // 类别不能为空
                throw new IllegalArgumentException("strategy's category cannot be empty. @see " + strategy.getClass().getCanonicalName());
            }
            int retainPrefixCount = strategy.retainPrefixCount();
            if (retainPrefixCount < 0) {
                // 类别不能为空
                throw new IllegalArgumentException("retainPrefixCount cannot allow less than zero. @see " + strategy.getClass().getCanonicalName());
            }
            int retainSuffixCount = strategy.retainSuffixCount();
            if (retainSuffixCount < 0) {
                // 类别不能为空
                throw new IllegalArgumentException("retainSuffixCount cannot allow less than zero. @see " + strategy.getClass().getCanonicalName());
            }
        });
    }
}
